/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年8月14日
 */
package com.massyframework.beanskin.maven.plugin;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.List;
import java.util.Objects;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.maven.model.Dependency;
import org.apache.maven.model.Exclusion;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * PomXml写入器
 * 
 * @author huangkh
 *
 */
public class PomXmlWriter {

	private File pomFile;

	/**
	 * 
	 */
	public PomXmlWriter(File pomFile) {
		this.pomFile = Objects.requireNonNull(pomFile, "\"pomFile\" cannot be null.");
	}

	public void appendDependency(List<Dependency> dependencies) throws Exception{
		InputStream inputStream = null;
		try {
			inputStream = new FileInputStream(this.pomFile);
			Document doc = this.createDocument(inputStream);
			
			NodeList nodes = doc.getElementsByTagName("project");
			Node projectNode = this.foundNodeByName(nodes, "project");
			Node depsNode = this.foundNodeByName(projectNode.getChildNodes(), "dependencies");
			if (depsNode == null) {
				depsNode = doc.createTextNode("dependencies");
				projectNode.appendChild(depsNode);
			}
			
			Node common = doc.createComment("beanskin-maven-plugin生成");
			depsNode.appendChild(common);
			
			for (Dependency dependency : dependencies) {
				this.appendDependencyNode(depsNode, dependency, doc);
			}

			this.writeDocument(doc);
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
					inputStream = null;
				} catch (Exception e) {

				}
			}
		}

	}

	protected void writeDocument(Document doc) throws TransformerException, IOException {

		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource source = new DOMSource(doc);

		FileWriter writer = null;
		try {
			writer =new FileWriter(this.pomFile);
			StreamResult result = new StreamResult(writer);
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(
					"{http://xml.apache.org/xslt}indent-amount", "2");
			transformer.transform(source, result);
		}finally {
			if (writer != null) {
				try {
					writer.close();
				}catch(Exception e) {
					
				}
			}
		}
	}

	/**
	 * 添加依赖节点
	 * 
	 * @param depsNode   {@link Node},依赖项目集节点
	 * @param dependency {@link Dependency},依赖项目
	 * @param doc        {@link Document}
	 */
	protected void appendDependencyNode(Node depsNode, Dependency dependency, Document doc) {	
		Element element = doc.createElement("dependency"); 
		Node depNode =(Node)element; 
		depsNode.appendChild(depNode);
		 
		element = doc.createElement("groupId");
		Node node = (Node)element;
		node.setTextContent(dependency.getGroupId());
		depNode.appendChild(node);
		
		
		element = doc.createElement("artifactId");
		node = (Node)element;
		node.setTextContent(dependency.getArtifactId());
		depNode.appendChild(node);
		
		element = doc.createElement("version");
		node = (Node)element;
		node.setTextContent(dependency.getVersion());
		depNode.appendChild(node);
		
		
		if (!isEmptyOrDefaultValue(dependency.getScope(), "compile")) {
			element = doc.createElement("scope");
			node = (Node)element;
			node.setTextContent(dependency.getScope());
			depNode.appendChild(node);
		}

		if (!isEmptyOrDefaultValue(dependency.getOptional(),"true")) {
			element = doc.createElement("optional");
			node = (Node)element;
			node.setTextContent(dependency.getOptional());
			depNode.appendChild(node);
		}

		if (!isEmpty(dependency.getClassifier())) {
			element = doc.createElement("classifier");
			node = (Node)element;
			node.setTextContent(dependency.getClassifier());
			depNode.appendChild(node);
		}

		if (!isEmpty(dependency.getSystemPath())) {
			element = doc.createElement("systemPath");
			node = (Node)element;
			node.setTextContent(dependency.getSystemPath());
			depNode.appendChild(node);
		}

		if (!isEmptyOrDefaultValue(dependency.getType(), "jar")) {
			
			element = doc.createElement("type");
			node = (Node)element;
			node.setTextContent(dependency.getType());
			depNode.appendChild(node);
		}
		
		if (dependency.getExclusions() != null) {
			if (!dependency.getExclusions().isEmpty()) {
				element = doc.createElement("exclusions");
				Node exclusionsNode = (Node)element;
				depNode.appendChild(exclusionsNode);
	
				List<Exclusion> exclusions = dependency.getExclusions();
				for (Exclusion exclusion : exclusions) {
					this.appendExclusion(exclusionsNode, exclusion, doc);
				}
			}
		}
	}

	/**
	 * 添加依赖项目的排斥节点
	 * 
	 * @param depNode   {@link Node},依赖节点
	 * @param exclusion {@link Exclusion},排斥项
	 * @param doc       {@link Document}
	 */
	protected void appendExclusion(Node exclusionsNode, Exclusion exclusion, Document doc) {		
		Element element = doc.createElement("exclusion"); 
		Node exclusionNode =(Node)element; 
		exclusionsNode.appendChild(exclusionNode);

		element = doc.createElement("groupId");
		Node node = (Node)element;
		node.setTextContent(exclusion.getGroupId());
		exclusionNode.appendChild(node);

		element = doc.createElement("artifactId");
		node = (Node)element;
		node.setTextContent(exclusion.getArtifactId());
		exclusionNode.appendChild(node);
	}

	/**
	 * 在{@code nodes}的子节点中查找名称为{@code nodeName}的节点
	 * 
	 * @param nodes    {@link NodeList}
	 * @param nodeName {@link String}
	 * @return {@link Node},可能返回null.
	 */
	protected Node foundNodeByName(NodeList nodes, String nodeName) {
		int size = nodes.getLength();
		for (int i = 0; i < size; i++) {
			Node child = nodes.item(i);
			if (child.getNodeName().equals(nodeName)) {
				return child;
			}
		}

		return null;
	}

	/**
	 * 创建Xml Document对象
	 * 
	 * @param inputStream 输入流
	 * @return {@link Document}
	 * @throws ParseException 解析发生错误抛出的异常
	 * @throws IOException    IO读写发生异常抛出的异常
	 * @throws SAXException   SAX解析发生错误抛出的异常
	 */
	protected final Document createDocument(InputStream inputStream) throws Exception, IOException {

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		return builder.parse(inputStream);
	}
	
	/**
	 * 判断字符串是否为empty或者缺省值
	 * @param text
	 * @param defaultValue
	 * @return
	 */
	protected boolean isEmptyOrDefaultValue(String text, String defaultValue) {
		if (text == null) return true;
		text = text.trim();
		if (text.length() == 0) return true;
		
		return text.equals(defaultValue);
	}
	
	/**
	 * 判断字符串是否为empty
	 * @param text
	 * @return
	 */
	protected boolean isEmpty(String text) {
		if (text == null) return true;
		text = text.trim();
		if (text.length() == 0) return true;
		
		return false;
	}
}
